package phase1.part2;

import java.lang.Comparable;
import java.util.ArrayList;
import java.util.List;

/**
 * A State contains all of the necessary information to place it on an NFA or DFA.
 * 
 * @author Devin Wang
 * @version Nov 13, 2012
 */
public class State implements Comparable{
	private String label;
	private boolean accepting;
	private String tokenClass;
	private List<Transition> transitions;
	
	/**
	 * Create a new state.
	 * @param label The label of the state
	 */
	public State(String label) {
		this.label = label;
		accepting = false;
		tokenClass = "";
		transitions = new ArrayList<>();
	}

	/**
	 * Get whether or not the state is an accepting state.
	 * @return <CODE>true</CODE> if it is accepting, <CODE>false</CODE> if not
	 */
	public boolean getAccepting() {
		return accepting;
	}

	/**
	 * Set the state's accepting status.
	 * @param accepting A boolean whose value is <CODE>true</CODE> if the state is to be accepting, and <CODE> false </CODE> if not.
	 */
	public void setAccepting(boolean accepting) {
		this.accepting = accepting;
	}

	/**
	 * Get the state's accepting token class.
	 * @return the state's accepting token class if it is accepting, <CODE>null</CODE> if it is not.
	 */
	public String getTokenClass() {
		return tokenClass;
	}

	/**
	 * Set the state's accepting token class.
	 * @param tokenClass The new token class
	 */
	public void setTokenClass(String tokenClass) {
		this.tokenClass = tokenClass;
	}

	/**
	 * Get the state's label.
	 * @return label
	 */
	public String getLabel() {
		return label;
	}
    
	/**
	 * Set the state's label
	 * @param label The new label
	 */
    public void setLabel(String label){
        this.label = label;
    }
	
    /**
     * Add a Transition to the state's transitions list.
     * @param transition The transition to add.
     */
	public void addTransition(Transition transition) {
		transitions.add(transition);
	}
    
	/**
	 * Find a state's transition using the desired end state.
	 * @param end The end state to use for the search
	 * @return The transition pointing to the end state if it exists, <CODE>null</CODE> if one does not.
	 */
    public Transition findTransition(State end) {
        for (Transition t : transitions) {
            if(t.getEnd().equals(end)){
                return t;
            }
        }
        return null;
    }
    
    /**
     * Add a character to the transition's pattern.
     * @param end The end state of the transition to update
     * @param c The character to add
     * @return <CODE>true</CODE> if the transition already existed, <CODE>false</CODE> if one does not.
     */
    public boolean updateTransitions(State end, char c) {
        boolean alreadyExists = false;
        for (Transition t : transitions) {
            if(t.getEnd().equals(end)){
                t.add(c);
                alreadyExists = true;
            }
        }
        if(!alreadyExists){
            String pattern = Character.toString(c);
            Transition move = new Transition(end, new Pattern(pattern));
            transitions.add(move);
        }
        return alreadyExists;
    }
	
    /**
     * Get the state's transitions.
     * @return transitions
     */
	public List<Transition> getTransitions() {
		return transitions;
	}
    
	/**
	 * Checks if a state has a transition that moves on a certain character.
	 * @param c The character to check for
	 * @return <CODE>true</CODE> if such a transition exists, <CODE>false</CODE> if not
	 */
    public boolean accepts(char c){
        for (Transition t : transitions){
            if(t.accepts(c)){
                return true;
            }
        }
        return false;
    }
    
    @Override
    public int compareTo(Object obj) {
    	return this.getLabel().compareTo(((State)obj).getLabel());
    }

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((label == null) ? 0 : label.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof State))
			return false;
		State other = (State) obj;
		if (label == null) {
			if (other.getLabel() != null)
				return false;
		} else if (!label.equals(other.getLabel()))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "State [label=" + label + ", accepting=" + accepting
				+ ", tokenClass=" + tokenClass + "]";
	}
}